module klang.编译.系统.系统_;
import 全局;
import std.conv: ·to = to;
import std.functional: ·fd = toDelegate;
import 运行时 = core.runtime·temp;
import 文件 = std.file·temp;
import 时间 = std.datetime.systime·temp;
import 时间日期 = std.datetime.date·temp;
import 持续时间 = core.time·temp;
import 进程 = std.process·temp;
import 同步 = core.sync.semaphore·temp;
import 编译数字 = 编译数字.编译;
import 核心 = klang.编译.核心.核心_;
import 路径模块 = klang.编译.路径.路径_;
import 工具 = klang.编译.工具.工具_;
import std.file·temp: 模式 = SpanMode;
import klang.编译.类型.枚举值.枚举值_: 比较结果_;
alias 文件监控回调_ = void delegate(dstring, 文件监控事件种类_);
alias 目录监控回调_ = void delegate(dstring);
alias 监控文件程序主机接口_ = 文件监控基类_ delegate(dstring, 文件监控回调_, int);
alias 监控目录程序主机接口_ = 文件监控基类_ delegate(dstring, 目录监控回调_, bool);
时间.SysTime 零值时间;
工具.文件系统条目_ 空文件系统条目;
klang.编译.系统.系统_.监控文件主机基类_ 基础监控主机;
klang.编译.系统.系统_.扩展监控文件_ 空监控文件;
klang.编译.系统.系统_.级别_ 轮询间隔_;
klang.编译.系统.系统_.级别_ 默认检查等级;
核心.词典_!(int) 轮询块大小;
核心.词典_!(int) 未更改的轮询块大小;
/*@internal*/
dstring[3u] 忽略路径组;
bool 是使用区分大小写文件系统 = false;
dstring 新行 = "\r\n"d;
bool 是小端 = false;
klang.编译.系统.系统_.监控文件程序主机接口_ 使用的文件监控;
klang.编译.系统.系统_.系统_ sys;
static this() {
    零值时间 = 时间.SysTime();
    空文件系统条目 = new 工具.文件系统条目_();
    基础监控主机 = new klang.编译.系统.系统_.监控文件主机基类_();
    空监控文件 = new klang.编译.系统.系统_.扩展监控文件_(""d, (dstring _, 文件监控事件种类_ __) { }, .零值时间, 0);
    轮询间隔_ = new klang.编译.系统.系统_.级别_(250, 500, 2000);
    默认检查等级 = new klang.编译.系统.系统_.级别_(32, 64, 256);
    轮询块大小 = .创建基于轮询间隔的级别(.默认检查等级);
    未更改的轮询块大小 = .创建基于轮询间隔的级别(.默认检查等级);
    忽略路径组 = ["/模块/."d, "/.git"d, "/.#"d];
    使用的文件监控 = .按名称创建每个单个文件监控(.创建动态优先级轮询监视文件主机(.基础监控主机), .是使用区分大小写文件系统);
    sys = new klang.编译.系统.系统_.系统_();
}
static this() {
    version(Windows) {
        .是使用区分大小写文件系统 = false;
    }
    else version(OSX) {
        .是使用区分大小写文件系统 = false;
    }
    else version(Posix) {
        .是使用区分大小写文件系统 = true;
        .新行 = "\n"d;
    }
    version(LittleEndian) {
        .是小端 = true;
    }
}
enum 文件监控事件种类_ : int {
    创建的 = 0,
    改变的 = 1,
    删除的 = 2
}
class 监控文件主机基类_ {
    this() { }
    bool 设置时间输出(void delegate() 回调, long 延时 = 0) {
        return .设置时间输出(cast(void delegate())(回调), 延时);
    }
    时间.SysTime 获取修改时间(dstring 路径) {
        return .获取修改时间(路径);
    }
}
class 监控文件_ {
    dstring 文件名称;
    文件监控回调_ 回调;
    时间.SysTime 修改时间;
    this() { }
    this(dstring 文件名, 文件监控回调_ 回调函数, 时间.SysTime 修改的时间) {
        this.文件名称 = 文件名;
        this.回调 = cast(文件监控回调_)(回调函数);
        this.修改时间 = 修改的时间;
    }
}
class 扩展监控文件_ : 监控文件_ {
    bool 是关闭的;
    int 未改变投票数;
    this() { }
    this(dstring 文件名, 文件监控回调_ 回调函数, 时间.SysTime 修改的时间, int 未改变投票) {
        super(文件名, cast(文件监控回调_)(回调函数), 修改的时间);
        this.未改变投票数 = 未改变投票;
        this.是关闭的 = true;
    }
}
class 文件监控基类_ {
    this() { }
    void 关闭() { }
}
class 文件监控_ : 文件监控基类_ {
    扩展监控文件_ 被监控文件;
    扩展监控文件_[] 受监控的文件组;
    this() { }
    this(扩展监控文件_ 监控文件, 扩展监控文件_[] 监控的文件组) {
        this.被监控文件 = 监控文件;
        this.受监控的文件组 = 监控的文件组;
    }
    override void 关闭() {
        this.被监控文件.是关闭的 = true;
        核心.无序删除项目!(扩展监控文件_)(this.受监控的文件组, this.被监控文件);
    }
}
class 扩展的文件监控_ : 文件监控基类_ {
    核心.词典_!(单文件监控者_) 缓存;
    核心.词典_!(文件监控回调_[]) 回调缓存;
    dstring 路径;
    文件监控回调_ 回调;
    this() { }
    this(核心.词典_!(单文件监控者_) 缓存词典, 核心.词典_!(文件监控回调_[]) 回调词典, dstring _路径, 文件监控回调_ _回调) {
        this.缓存 = 缓存词典;
        this.回调缓存 = 回调词典;
        this.路径 = _路径;
        this.回调 = cast(文件监控回调_)(_回调);
    }
    override void 关闭() {
        this.缓存.删除值(this.路径);
        if (this.回调缓存.具有(this.路径)) {
            文件监控回调_[] 回调组 = this.回调缓存.获取(this.路径);
            核心.无序删除项目!(文件监控回调_)(回调组, this.回调);
        }
        if (this.缓存.具有(this.路径)) {
            单文件监控者_ 监控者 = this.缓存.获取(this.路径);
            监控者.监控者.关闭();
        }
    }
}
class 级别_ {
    int 低;
    int 中;
    int 高;
    this() { }
    this(int _低, int _中, int _高) {
        this.低 = _低;
        this.中 = _中;
        this.高 = _高;
    }
}
class 轮询间隔队列_ {
    扩展监控文件_[] 数据;
    int 轮询间隔_;
    size_t 投票索引;
    bool 预定的投票;
    this() { }
    this(扩展监控文件_[] _数据, int _轮询间隔, size_t _投票索引 = 0, bool _预定的投票 = false) {
        this.数据 = _数据;
        this.轮询间隔_ = _轮询间隔;
        this.投票索引 = _投票索引;
        this.预定的投票 = _预定的投票;
    }
}
class 单文件监控者_ {
    文件监控基类_ 监控者;
    int 引用数量;
    this() { }
    this(文件监控基类_ 监控, int 数量 = 0) {
        this.监控者 = 监控;
        this.引用数量 = 数量;
    }
}
class 递归目录监控程序主机接口_ {
    监控目录程序主机接口_ 监控目录;
    bool 使用区分大小写的文件名称;
    this() { }
    this(监控目录程序主机接口_ 目录监控, bool _使用区分大小写的文件名称) {
        this.监控目录 = cast(监控目录程序主机接口_)(目录监控);
        this.使用区分大小写的文件名称 = _使用区分大小写的文件名称;
    }
    bool 目录存在(dstring 路径) { return false; }
    dstring[] 获取可访问的排序子目录(dstring 目录) {
        return [];
    }
    dstring 真实路径(dstring 路径) { return ""d; }
}
class 子目录监控_ : 文件监控基类_ {
    dstring 目录名称;
    dstring 目录路径;
    核心.词典_!(主机目录监控者_) 缓存;
    核心.词典_!(目录监控回调_[]) 回调缓存;
    目录监控回调_ 回调;
    this() { }
    this(dstring _目录, dstring _目录路径, 核心.词典_!(主机目录监控者_) _缓存, 核心.词典_!(目录监控回调_[]) _回调缓存, 目录监控回调_ _回调 = ··null!(目录监控回调_)) {
        this.目录名称 = _目录;
        this.目录路径 = _目录路径;
        this.缓存 = _缓存;
        this.回调缓存 = _回调缓存;
        this.回调 = cast(目录监控回调_)(_回调);
    }
    override void 关闭() {
        if ((this.回调 !is ··null!(目录监控回调_)) && this.缓存.具有(this.目录路径)) {
            主机目录监控者_ 目录监控 = this.缓存.获取(this.目录路径);
            if (this.回调缓存.具有(this.目录路径)) {
                目录监控回调_[] 回调组 = this.回调缓存.获取(this.目录路径);
                核心.无序删除项目!(目录监控回调_)(回调组, this.回调);
            }
            --目录监控.引用数量;
            if (目录监控.引用数量 > 0) {
                return;
            }
            this.缓存.删除值(this.目录路径);
            目录监控.监控者.关闭();
            核心.循环每个!(子目录监控_, void*)(目录监控.子监控者组, (子目录监控_ v, size_t i) {
                ((子目录监控_ 监控, size_t _) { return 监控.关闭(); })(v, i);
                return null;
            });
        }
    }
}
class 主机目录监控者_ {
    文件监控基类_ 监控者;
    子目录监控_[] 子监控者组;
    int 引用数量;
    this() { }
    this(文件监控基类_ _监控者, 子目录监控_[] _子监控者组, int _引用数量 = 0) {
        this.监控者 = _监控者;
        this.子监控者组 = _子监控者组;
        this.引用数量 = _引用数量;
    }
}
class 系统_ {
    dstring[] 实参;
    bool 是使用区分大小写文件系统;
    dstring 新行;
    bool 是小端;
    监控文件程序主机接口_ 监控文件;
    监控目录程序主机接口_ 监控目录;
    this() {
        this.实参 = .实参();
        this.是使用区分大小写文件系统 = .是使用区分大小写文件系统;
        this.新行 = .新行;
        this.是小端 = .是小端;
        this.监控文件 = cast(监控文件程序主机接口_)(.使用的文件监控);
        this.监控目录 = .创建监控目录使用监控主机(.创建动态优先级轮询监视文件主机(.基础监控主机));
    }
    void 写出(dstring s) {
        .·writeln(·to!(string)(s));
    }
    dstring 读文件(dstring 文件名称) {
        if (文件.exists(文件名称)) {
            return 文件.读取文本(文件名称);
        }
        return ··null!(dstring);
    }
    void 写文件(dstring 文件名称, dstring 内容) {
        dstring 路径 = 路径模块.正规化路径(文件名称);
        if (文件.exists(路径)) {
            文件.写入文本(文件名称, 内容);
        }
        else {
            路径 = 路径模块.获取目录路径(文件名称);
            文件.制作目录递归(路径);
            文件.写入文本(文件名称, 内容);
        }
    }
    bool 文件存在(dstring 文件名) {
        return 文件.exists(文件名) && 文件.isFile(文件名);
    }
    bool 目录存在(dstring 路径) {
        return 文件.exists(路径) && 文件.isDir(路径);
    }
    void 创建目录(dstring 目录名称) {
        if (!文件.exists(目录名称)) {
            文件.制作目录递归(目录名称);
        }
    }
    dstring 获取执行文件路径() {
        return 文件.获取当前可执行文件路径();
    }
    dstring 获取当前目录() {
        return 文件.获取当前目录();
    }
    dstring[] 获取目录组(dstring 路径) {
        文件.DirEntry[] 目录组 = 核心.过滤!(文件.DirEntry)(文件.读目录(路径, 模式.depth), (文件.DirEntry 目录) { return 目录.isDir(); });
        if (目录组 !is ··null!(文件.DirEntry[])) {
            return 核心.映射!(文件.DirEntry, dstring)(目录组, (文件.DirEntry 目录, size_t _) { return 路径模块.正规化路径(·to!(dstring)(目录.name())); });
        }
        return ··null!(dstring[]);
    }
    dstring 绝对路径(dstring 路径, dstring 根路径 = .获取当前目录()) {
        return 路径模块.正规化路径(路径模块.组合路径(根路径, [路径]));
    }
    时间.SysTime 获取修改时间(dstring 路径) {
        return .获取修改时间(路径);
    }
    void 设置修改时间(dstring 路径, 时间.SysTime 新时间) {
        文件.setTimes(路径, 新时间, 新时间);
    }
    void 删除文件(dstring 文件名) {
        if (文件.exists(文件名)) {
            文件.remove(文件名);
        }
    }
    void 删除目录(dstring 路径) {
        if (文件.exists(路径) && 文件.isDir(路径)) {
            文件.删除目录递归(路径);
        }
    }
    dstring[] 读目录(dstring 路径, dstring[] 扩展组 = [], dstring[] 排除组 = [], dstring[] 包含组 = [], int 深度 = 0) {
        return .读目录(路径, 扩展组, 排除组, 包含组, 深度);
    }
    时间.SysTime 现在() {
        return 时间.Clock.currTime();
    }
    dstring 临时目录() {
        return 文件.临时目录();
    }
}
struct 进程环境选项_ {
    dstring 当前目录;
    this(dstring 目录) {
        this.当前目录 = 目录;
    }
}
dstring 生成DJB2哈希(dstring 数据) {
    long acc = 5381L;
    {
        for (int i = 0; i < 数据.length; ++i) {
            acc = ((acc << 5) + acc) + (cast(int)(数据[i]));
        }
    }
    return ·to!(dstring)(acc);
}
dstring[] 实参() {
    string[] arr = 运行时.Runtime.args();
    return 核心.映射定义的!(string, dstring)(arr, (string v, size_t _) {
        if (v !is ··null!(string)) {
            return ·to!(dstring)(v);
        }
        return ··null!(dstring);
    });
}
bool 设置时间输出(void delegate() 回调, long 延时 = 0) {
    同步.Semaphore 信号 = new 同步.Semaphore();
    if (!信号.wait(持续时间.msecs(延时))) {
        回调();
        return true;
    }
    return false;
}
dstring 获取当前目录() {
    return 文件.获取当前目录();
}
时间.SysTime 获取修改时间(dstring 路径) {
    时间.SysTime 创建时间;
    时间.SysTime 修改时间;
    文件.getTimes(路径, 创建时间, 修改时间);
    if (修改时间 > 创建时间) {
        return 修改时间;
    }
    return 创建时间;
}
核心.词典_!(int) 创建基于轮询间隔的级别(级别_ 级别) {
    return new 核心.词典_!(int)([·to!(dstring)(轮询间隔_.低) : 级别.低, ·to!(dstring)(轮询间隔_.中) : 级别.中, ·to!(dstring)(轮询间隔_.高) : 级别.高]);
}
void 设置自定义轮询值() {
    核心.词典_!(int) delegate(dstring, 级别_) 获取自定义轮询基等级;
    dstring delegate(dstring, dstring) 获取级别;
    级别_ delegate(dstring) 获取自定义级别;
    bool delegate(dstring, 级别_) 设置自定义级别;
    bool 轮询间隔已更改;
    核心.词典_!(int) 新轮询块大小;
    核心.词典_!(int) 新未更改的轮询块大小;
    核心.词典_!(int) 获取自定义轮询基等级·func(dstring 基变量, 级别_ 默认的) {
        级别_ 自定义的;
        自定义的 = 获取自定义级别(基变量);
        if (轮询间隔已更改) {
            if (自定义的.低 == 0) {
                自定义的.低 = 默认的.低;
            }
            if (自定义的.中 == 0) {
                自定义的.中 = 默认的.中;
            }
            if (自定义的.高 == 0) {
                自定义的.高 = 默认的.高;
            }
            return .创建基于轮询间隔的级别(自定义的);
        }
        return ··null!(核心.词典_!(int));
    }
    获取自定义轮询基等级 = &获取自定义轮询基等级·func;
    dstring 获取级别·func(dstring 环境变量, dstring 级别) {
        dstring 变量名;
        变量名 = ``d ~ 环境变量 ~ `_`d ~ 核心.文本转大写(级别) ~ ``d;
        if (进程.是否有环境变量(变量名)) {
            return 进程.环境()[变量名];
        }
        return ··null!(dstring);
    }
    获取级别 = &获取级别·func;
    级别_ 获取自定义级别·func(dstring 基变量) {
        void delegate(dstring) 设置级别;
        级别_ 自定义;
        void 设置级别·func(dstring 级别) {
            dstring 自定义级别;
            自定义级别 = 获取级别(基变量, 级别);
            if (自定义级别 !is ··null!(dstring)) {
                if (级别 == "低"d) {
                    自定义.低 = 编译数字.编译!(int)(自定义级别, 10);
                }
                else if (级别 == "中"d) {
                    自定义.中 = 编译数字.编译!(int)(自定义级别, 10);
                }
                else if (级别 == "高"d) {
                    自定义.高 = 编译数字.编译!(int)(自定义级别, 10);
                }
            }
        }
        设置级别 = &设置级别·func;
        自定义 = new 级别_(0, 0, 0);
        设置级别("低"d);
        设置级别("中"d);
        设置级别("高"d);
        return 自定义;
    }
    获取自定义级别 = &获取自定义级别·func;
    bool 设置自定义级别·func(dstring 基变量, 级别_ 级别) {
        void delegate(dstring) 设置;
        级别_ 自定义;
        void 设置·func(dstring 字段) {
            if (字段 == "低"d && 自定义.低 != 0) {
                级别.低 = 自定义.低;
            }
            else if (字段 == "中"d && 自定义.中 != 0) {
                级别.中 = 自定义.中;
            }
            else if (字段 == "高"d && 自定义.高 != 0) {
                级别.高 = 自定义.高;
            }
        }
        设置 = &设置·func;
        自定义 = 获取自定义级别(基变量);
        if (自定义.低 != 0 || 自定义.中 != 0 || 自定义.高 != 0) {
            设置("低"d);
            设置("中"d);
            设置("高"d);
            return true;
        }
        return false;
    }
    设置自定义级别 = &设置自定义级别·func;
    轮询间隔已更改 = 设置自定义级别("KSC_WATCH_POLLINGINTERVAL"d, .轮询间隔_);
    新轮询块大小 = 获取自定义轮询基等级("KSC_WATCH_POLLINGCHUNKSIZE"d, .默认检查等级);
    if ((新轮询块大小 !is null) && (新轮询块大小.原型 !is ··null!(int[dstring]))) {
        .轮询块大小 = 新轮询块大小;
    }
    新未更改的轮询块大小 = 获取自定义轮询基等级("KSC_WATCH_UNCHANGEDPOLLTHRESHOLDS"d, .默认检查等级);
    if ((新未更改的轮询块大小 !is null) && (新未更改的轮询块大小.原型 !is ··null!(int[dstring]))) {
        .未更改的轮询块大小 = 新未更改的轮询块大小;
    }
}
/**
 * Returns true if file status changed
 */
/*@internal*/
bool 正则更改监控文件状态(监控文件_ 监控文件, 时间.SysTime 修改时间) {
    时间.SysTime 旧时间 = 监控文件.修改时间;
    时间.SysTime 新时间 = 修改时间;
    if (旧时间 != 新时间) {
        监控文件.修改时间 = 修改时间;
        监控文件.回调(监控文件.文件名称, .获取文件监控事件种类(旧时间, 新时间));
        return true;
    }
    return false;
}
/*@internal*/
文件监控事件种类_ 获取文件监控事件种类(时间.SysTime 旧时间, 时间.SysTime 新时间) {
    return 旧时间 == .零值时间 ? 文件监控事件种类_.创建的 : 新时间 == .零值时间 ? 文件监控事件种类_.删除的 : 文件监控事件种类_.改变的;
}
监控文件程序主机接口_ 创建动态优先级轮询监视文件主机(监控文件主机基类_ 主机) {
    轮询间隔队列_ delegate(int) 创建轮询间隔队列;
    轮询间隔队列_ delegate(int) 选择相应队列;
    时间.SysTime delegate(dstring) 获取时间;
    void delegate(轮询间隔队列_) 请求轮询间隔队列;
    void delegate(int) 安排下次投票;
    void delegate(int) 如果尚未安排请安排下一次;
    void delegate(扩展监控文件_, int) 添加到轮询间隔队列;
    void delegate(扩展监控文件_) 添加改变文件到低轮询间隔队列;
    size_t delegate(扩展监控文件_[], size_t, size_t, size_t) 请求队列;
    void delegate(轮询间隔队列_) 请求低请求轮询队列;
    文件监控_ delegate(dstring, 文件监控回调_, int) 监控文件;
    扩展监控文件_[] 受监控的文件组;
    扩展监控文件_[] 上次投票时已更改的文件;
    轮询间隔队列_ 低轮询间隔队列;
    轮询间隔队列_ 中轮询间隔队列;
    轮询间隔队列_ 高轮询间隔队列;
    轮询间隔队列_ 创建轮询间隔队列·func(int 轮询间隔) {
        return new 轮询间隔队列_([], 轮询间隔);
    }
    创建轮询间隔队列 = &创建轮询间隔队列·func;
    轮询间隔队列_ 选择相应队列·func(int 级别) {
        if (轮询间隔_.低 == 级别) {
            return 低轮询间隔队列;
        }
        if (轮询间隔_.中 == 级别) {
            return 中轮询间隔队列;
        }
        return 高轮询间隔队列;
    }
    选择相应队列 = &选择相应队列·func;
    时间.SysTime 获取时间·func(dstring 文件名) {
        return 主机.获取修改时间(文件名);
    }
    获取时间 = &获取时间·func;
    void 请求轮询间隔队列·func(轮询间隔队列_ 队列) {
        队列.投票索引 = 请求队列(队列.数据, 队列.轮询间隔_, 队列.投票索引, (轮询块大小.具有(·to!(dstring)(队列.轮询间隔_)) ? 轮询块大小.获取(·to!(dstring)(队列.轮询间隔_)) : 32));
        // Set the next polling index and timeout
        if (队列.数据.length != 0) {
            安排下次投票(队列.轮询间隔_);
        }
        else {
            队列.预定的投票 = false;
        }
    }
    请求轮询间隔队列 = &请求轮询间隔队列·func;
    void 安排下次投票·func(int 间隔) {
        选择相应队列(间隔).预定的投票 = 主机.设置时间输出(() {
            间隔 == 轮询间隔_.低 ? 请求低请求轮询队列(选择相应队列(间隔)) : 请求轮询间隔队列(选择相应队列(间隔));
        }, 间隔);
    }
    安排下次投票 = &安排下次投票·func;
    void 如果尚未安排请安排下一次·func(int 间隔) {
        if (!选择相应队列(间隔).预定的投票) {
            安排下次投票(间隔);
        }
    }
    如果尚未安排请安排下一次 = &如果尚未安排请安排下一次·func;
    void 添加到轮询间隔队列·func(扩展监控文件_ 文件, int 间隔) {
        .追加!(扩展监控文件_)(选择相应队列(间隔).数据, 文件);
        如果尚未安排请安排下一次(间隔);
    }
    添加到轮询间隔队列 = &添加到轮询间隔队列·func;
    void 添加改变文件到低轮询间隔队列·func(扩展监控文件_ 文件) {
        .追加!(扩展监控文件_)(上次投票时已更改的文件, 文件);
        如果尚未安排请安排下一次(轮询间隔_.低);
    }
    添加改变文件到低轮询间隔队列 = &添加改变文件到低轮询间隔队列·func;
    size_t 请求队列·func(扩展监控文件_[] 队列, size_t 间隔, size_t 索引, size_t 块大小) {
        void delegate() 下个索引;
        size_t 需要查看;
        size_t 定义值复制到索引;
        void 下个索引·func() {
            ++索引;
            if (索引 == 队列.length) {
                if (定义值复制到索引 < 索引) {
                    // There are holes from nextDefinedValueIndex to end of queue, change queue size
                    队列.length = 定义值复制到索引;
                }
                索引 = 0;
                定义值复制到索引 = 0;
            }
        }
        下个索引 = &下个索引·func;
        需要查看 = 队列.length;
        定义值复制到索引 = 索引;
        {
            for (int 测验索引 = 0; 测验索引 < 块大小 && 需要查看 > 0; 下个索引(), --需要查看) {
                扩展监控文件_ 监控文件;
                bool 文件改变;
                监控文件 = 队列[索引];
                if (监控文件 is ··null!(扩展监控文件_)) {
                    continue;
                }
                else if (监控文件.是关闭的) {
                    队列[索引] = .空监控文件;
                    continue;
                }
                ++测验索引;
                文件改变 = .正则更改监控文件状态(监控文件, 获取时间(监控文件.文件名称));
                if (监控文件.是关闭的) {
                    // Closed watcher as part of callback
                    队列[索引] = .空监控文件;
                }
                else if (文件改变) {
                    监控文件.未改变投票数 = 0;
                    // Changed files go to changedFilesInLastPoll queue
                    if (队列 != 上次投票时已更改的文件) {
                        队列[索引] = .空监控文件;
                        添加改变文件到低轮询间隔队列(监控文件);
                    }
                }
                else if (未更改的轮询块大小.具有(·to!(dstring)(间隔)) && 监控文件.未改变投票数 != 未更改的轮询块大小.获取(·to!(dstring)(间隔))) {
                    ++监控文件.未改变投票数;
                }
                else if (队列 == 上次投票时已更改的文件) {
                    // Restart unchangedPollCount for unchanged file and move to low polling interval queue
                    监控文件.未改变投票数 = 1;
                    队列[索引] = .空监控文件;
                    添加到轮询间隔队列(监控文件, 轮询间隔_.低);
                }
                else if (间隔 != 轮询间隔_.高) {
                    ++监控文件.未改变投票数;
                    队列[索引] = .空监控文件;
                    添加到轮询间隔队列(监控文件, 间隔 == 轮询间隔_.低 ? 轮询间隔_.中 : 轮询间隔_.高);
                }
                if (队列[索引] !is ··null!(扩展监控文件_)) {
                    // Copy this file to the non hole location
                    if (定义值复制到索引 < 索引) {
                        队列[定义值复制到索引] = 监控文件;
                        队列[索引] = .空监控文件;
                    }
                    ++定义值复制到索引;
                }
            }
        }
        // Return next poll index
        return 索引;
    }
    请求队列 = &请求队列·func;
    void 请求低请求轮询队列·func(轮询间隔队列_ 队列) {
        // Always poll complete list of changedFilesInLastPoll
        请求队列(上次投票时已更改的文件, 轮询间隔_.低, /*pollIndex*/ 0, 上次投票时已更改的文件.length);
        // Finally do the actual polling of the queue
        请求轮询间隔队列(队列);
        // Schedule poll if there are files in changedFilesInLastPoll but no files in the actual queue
        // as pollPollingIntervalQueue wont schedule for next poll
        if (!队列.预定的投票 && (上次投票时已更改的文件.length !is ··null!(size_t))) {
            安排下次投票(轮询间隔_.低);
        }
    }
    请求低请求轮询队列 = &请求低请求轮询队列·func;
    文件监控_ 监控文件·func(dstring 文件名, 文件监控回调_ 回调, int 默认轮询) {
        扩展监控文件_ 被监控文件;
        被监控文件 = new 扩展监控文件_(文件名, cast(文件监控回调_)(回调), 主机.获取修改时间(文件名), 0);
        .追加!(扩展监控文件_)(受监控的文件组, 被监控文件);
        添加到轮询间隔队列(被监控文件, 默认轮询);
        return new 文件监控_(被监控文件, 受监控的文件组);
    }
    监控文件 = &监控文件·func;
    受监控的文件组 = [];
    上次投票时已更改的文件 = [];
    低轮询间隔队列 = 创建轮询间隔队列(轮询间隔_.低);
    中轮询间隔队列 = 创建轮询间隔队列(轮询间隔_.中);
    高轮询间隔队列 = 创建轮询间隔队列(轮询间隔_.高);
    return cast(监控文件程序主机接口_)(监控文件);
}
监控文件程序主机接口_ 按名称创建每个单个文件监控(监控文件程序主机接口_ 监控文件, bool 使用区分大小写的文件名称) {
    扩展的文件监控_ delegate(dstring, 文件监控回调_, int) 单文件监控者;
    核心.词典_!(单文件监控者_) 缓存;
    核心.词典_!(文件监控回调_[]) 回调缓存;
    核心.获取正规文件名函数_ 规范文件名函数;
    扩展的文件监控_ 单文件监控者·func(dstring 文件名, 文件监控回调_ 回调, int 间隔) {
        dstring 路径;
        路径 = 规范文件名函数(文件名);
        if (缓存.具有(路径)) {
            单文件监控者_ 存在 = 缓存.获取(路径);
            ++存在.引用数量;
        }
        else {
            文件监控基类_ 文件监控 = 监控文件(文件名, cast(文件监控回调_)(回调), 间隔);
            缓存.p·设置(路径, new 单文件监控者_(文件监控));
        }
        if (!回调缓存.具有(路径)) {
            回调缓存.p·设置(路径, [cast(文件监控回调_)(回调)]);
        }
        else {
            .追加!(文件监控回调_)(回调缓存.获取(路径), 回调);
        }
        return new 扩展的文件监控_(缓存, 回调缓存, 路径, cast(文件监控回调_)(回调));
    }
    单文件监控者 = &单文件监控者·func;
    缓存 = 核心.创建词典!(单文件监控者_)();
    回调缓存 = 核心.创建词典!(文件监控回调_[])();
    规范文件名函数 = 核心.创建获取正规文件名函数(使用区分大小写的文件名称);
    return cast(监控文件程序主机接口_)(单文件监控者);
}
监控目录程序主机接口_ 创建递归目录监控者(递归目录监控程序主机接口_ 主机) {
    子目录监控_ delegate(dstring, 目录监控回调_, bool) 创建目录监控;
    void delegate(dstring, dstring) 更新子监控者;
    子目录监控_[] delegate(dstring, 子目录监控_[]) 监控子目录组;
    bool delegate(dstring) 是忽略路径;
    bool delegate(dstring, dstring) 是在路径;
    核心.词典_!(主机目录监控者_) 缓存;
    核心.词典_!(目录监控回调_[]) 回调缓存;
    比较结果_ delegate(dstring, dstring) 文件路径比较器;
    核心.获取正规文件名函数_ 规范文件名函数;
    子目录监控_ 创建目录监控·func(dstring 目录, 目录监控回调_ 回调 = ··null!(目录监控回调_), bool _ = ··null!(bool)) {
        dstring 目录路径;
        主机目录监控者_ 目录监控者;
        目录路径 = 规范文件名函数(目录);
        if (缓存.具有(目录路径)) {
            目录监控者 = 缓存.获取(目录路径);
            ++目录监控者.引用数量;
        }
        else {
            目录监控者 = new 主机目录监控者_(主机.监控目录(目录, (dstring 文件名称) {
                回调缓存.p·循环每个((目录监控回调_[] 回调组, dstring 根目录名称) {
                    if (根目录名称 == 目录路径 || 核心.开始包含(目录路径, 根目录名称)) {
                        核心.循环每个!(目录监控回调_, void*)(回调组, (目录监控回调_ v, size_t i) {
                            ((目录监控回调_ v, size_t _i) { return v(文件名称); })(v, i);
                            return null;
                        });
                    }
                });
                更新子监控者(目录, 目录路径);
            }, false), [], 1);
            缓存.p·设置(目录路径, 目录监控者);
            更新子监控者(目录, 目录路径);
        }
        if (回调 !is ··null!(目录监控回调_)) {
            if (!回调缓存.具有(目录)) {
                回调缓存.p·设置(目录, [cast(目录监控回调_)(回调)]);
            }
            else {
                .追加!(目录监控回调_)(回调缓存.获取(目录), 回调);
            }
        }
        return new 子目录监控_(目录, 目录路径, 缓存, 回调缓存, cast(目录监控回调_)(回调));
    }
    创建目录监控 = &创建目录监控·func;
    void 更新子监控者·func(dstring 目录名称, dstring 目录路径) {
        // Iterate through existing children and update the watches if needed
        if (缓存.具有(目录路径)) {
            主机目录监控者_ 父监控者 = 缓存.获取(目录路径);
            父监控者.子监控者组 = 监控子目录组(目录名称, 父监控者.子监控者组);
        }
    }
    更新子监控者 = &更新子监控者·func;
    /**
     * Watch the directories in the parentDir
     */
    子目录监控_[] 监控子目录组·func(dstring 父目录, 子目录监控_[] 现有子目录监控) {
        void delegate(dstring) 创建和添加子目录监控者;
        void delegate(子目录监控_, dstring) 添加子目录监控;
        子目录监控_[] 新子目录监控;
        dstring[] 文件组;
        /**
         * Create new childDirectoryWatcher and add it to the new ChildDirectoryWatcher list
         */
        void 创建和添加子目录监控者·func(dstring 子名称) {
            子目录监控_ 结果;
            结果 = 创建目录监控(子名称, ··null!(目录监控回调_), ··null!(bool));
            添加子目录监控(结果, ""d);
        }
        创建和添加子目录监控者 = &创建和添加子目录监控者·func;
        /**
         * Add child directory watcher to the new ChildDirectoryWatcher list
         */
        void 添加子目录监控·func(子目录监控_ 子监控, dstring _) {
            .追加!(子目录监控_)(新子目录监控, 子监控);
        }
        添加子目录监控 = &添加子目录监控·func;
        新子目录监控 = [];
        文件组 = 主机.目录存在(父目录) ? 核心.映射定义的!(dstring, dstring)(主机.获取可访问的排序子目录(父目录), (dstring 子, size_t _) {
            dstring 子全名 = 路径模块.获取正规化绝对路径(子, 父目录);
            // Filter our the symbolic link directories since those arent included in recursive watch
            // which is same behaviour when recursive: true is passed to fs.watch
            if (!是忽略路径(子全名) && 文件路径比较器(子全名, 路径模块.正规化路径(主机.真实路径(子全名))) == 比较结果_.等于) {
                return 子全名;
            }
            return ··null!(dstring);
        }) : [];
        核心.列举插入和删除!(dstring, 子目录监控_)(文件组, 现有子目录监控, (dstring 子目录, 子目录监控_ 子监控) { return 文件路径比较器(子目录, 子监控.目录名称); }, cast(void delegate(dstring))(创建和添加子目录监控者), (子目录监控_ 监控) { return 监控.关闭(); }, cast(void delegate(子目录监控_, dstring))(添加子目录监控));
        return 新子目录监控;
    }
    监控子目录组 = &监控子目录组·func;
    bool 是忽略路径·func(dstring 路径) {
        return 核心.包含一部分!(dstring)(.忽略路径组, (dstring 搜索路径) { return 是在路径(路径, 搜索路径); });
    }
    是忽略路径 = &是忽略路径·func;
    bool 是在路径·func(dstring 路径, dstring 搜索路径) {
        if (核心.文本包含(路径, 搜索路径)) {
            return true;
        }
        if (主机.使用区分大小写的文件名称) {
            return false;
        }
        return 核心.文本包含(规范文件名函数(路径), 搜索路径);
    }
    是在路径 = &是在路径·func;
    缓存 = 核心.创建词典!(主机目录监控者_)();
    回调缓存 = 核心.创建词典!(目录监控回调_[])();
    文件路径比较器 = 核心.获取文本比较器(主机.使用区分大小写的文件名称);
    规范文件名函数 = 核心.创建获取正规文件名函数(主机.使用区分大小写的文件名称);
    return cast(监控目录程序主机接口_)(创建目录监控);
}
监控目录程序主机接口_ 创建监控目录使用监控主机(监控文件程序主机接口_ 主机) {
    文件监控基类_ delegate(dstring, 目录监控回调_, bool) 监控主机;
    文件监控基类_ 监控主机·func(dstring 目录名称, 目录监控回调_ 回调 = ··null!(目录监控回调_), bool 递归 = ··null!(bool)) {
        return 主机(目录名称, (dstring _, 文件监控事件种类_ __) {
            if (回调 !is ··null!(目录监控回调_)) {
                回调(目录名称);
            }
        }, 轮询间隔_.中);
    }
    监控主机 = &监控主机·func;
    return cast(监控目录程序主机接口_)(监控主机);
}
dstring[] 读目录(dstring 路径, dstring[] 扩展组, dstring[] 排除组, dstring[] 包含组, int 深度) {
    return 工具.匹配文件组(路径, 扩展组, 排除组, 包含组, .是使用区分大小写文件系统, 文件.获取当前目录(), 深度, cast(工具.文件系统条目_ delegate(dstring))(·fd(&.获取可访问的文件系统条目)), (dstring 路径) { return 路径模块.正规化路径(路径模块.组合路径(文件.获取当前目录(), [路径])); });
}
工具.文件系统条目_ 获取可访问的文件系统条目(dstring path) {
    try {
        文件.DirEntry[] 目录条目 = 核心.排序!(文件.DirEntry)(文件.读目录(path, 模式.shallow), (文件.DirEntry a, 文件.DirEntry b) { return 核心.获取文本比较器(.是使用区分大小写文件系统)(·to!(dstring)(a.name()), ·to!(dstring)(b.name())); });
        dstring[] 文件组 = [];
        dstring[] 目录组 = [];
        foreach (文件.DirEntry 条目; 目录条目) {
            if (条目.isFile()) {
                文件组 = .追加!(dstring)(文件组, ·to!(dstring)(条目.name()));
            }
            else if (条目.isDir()) {
                目录组 = .追加!(dstring)(目录组, ·to!(dstring)(条目.name()));
            }
        }
        return new 工具.文件系统条目_(文件组, 目录组);
    }
    catch(Throwable e) {
        return .空文件系统条目;
    }
}
unittest {
    void main() {
        dstring 路径 = "d:/a/b/c"d;
        文件.制作目录递归(路径);
        监控文件主机基类_ 监控 = new 监控文件主机基类_();
        scope(exit) {
            if (文件.exists("d:/a"d)) {
                文件.删除目录递归("d:/a"d);
            }
        }
        dstring 文件1 = "d:/a/f1.txt"d;
        dstring 文件2 = "d:/a/b/f2.txt"d;
        dstring 文件3_1 = "d:/a/b/c/f3_1.txt"d;
        dstring 文件3_2 = "d:/a/b/c/f3_2.txt"d;
        assert(文件.exists(路径), "目录创建未成功."c);
        文件.写入文本(文件1, "文件1内容"d);
        dstring 文件1内容 = 文件.读取文本(文件1);
        监控.设置时间输出(() {
            时间.SysTime 修改时间 = 监控.获取修改时间(文件1);
            sys.写出("================================================================="d);
            sys.写出("============================klang================================"d);
            sys.写出(`===================`d ~ 修改时间.·to!(dstring)() ~ `==================`d);
        }, 100);
        文件.写入文本(文件2, "文件2内容"d);
        dstring 文件2内容 = 文件.读取文本(文件2);
        文件.写入文本(文件3_1, "文件3_1内容"d);
        dstring 文件3_1内容 = 文件.读取文本(文件3_1);
        文件.写入文本(文件3_2, "文件3_2内容"d);
        dstring 文件3_2内容 = 文件.读取文本(文件3_2);
        assert(文件1内容 == "文件1内容"d, "文件1错误消息"c);
        assert(文件2内容 == "文件2内容"d, "文件2错误消息"c);
        assert(文件3_1内容 == "文件3_1内容"d, "文件3_1错误消息"c);
        assert(文件3_2内容 == "文件3_2内容"d, "文件3_2错误消息"c);
        文件.DirEntry[] fs = 文件.读目录("d:/a"d, 模式.depth);
        assert(fs.length == 6, "'读目录' 错误"c);
        assert(fs[0].isFile(), "'读目录' 错误"c);
        assert(fs[1].isFile(), "'读目录' 错误"c);
        assert(!fs[2].isFile(), "'读目录' 错误"c);
        assert(fs[3].isFile(), "'读目录' 错误"c);
        assert(!fs[4].isFile(), "'读目录' 错误"c);
        assert(fs[5].isFile(), "'读目录' 错误"c);
        sys.写文件("d:/temp/klang/n.txt"d, "随意一部分内容"d);
        dstring 内容 = sys.读文件("d:/temp/klang/n.txt"d);
        assert(内容 == "随意一部分内容"d, "文件内容错误"c);
        scope(exit) {
            if (sys.文件存在(("d:/temp/klang/n.txt"d))) {
                sys.删除文件("d:/temp/klang/n.txt"d);
            }
            if (文件.exists("d:/temp/klang"d)) {
                文件.删除目录递归("d:/temp/klang"d);
            }
        }
        int[] arr = [2, 4, 5, 6, 1, 6, 7];
        int[] 排序后数组 = 核心.排序!(int)(arr, (int a, int b) {
            if (a == b) {
                return 比较结果_.等于;
            }
            if (a > b) {
                return 比较结果_.大于;
            }
            return 比较结果_.小于;
        });
        assert(排序后数组.length == 7, "'排序' 错误"c);
        assert(排序后数组[0] == 1, "'排序' 错误"c);
        assert(排序后数组[1] == 2, "'排序' 错误"c);
        assert(排序后数组[2] == 4, "'排序' 错误"c);
        assert(排序后数组[3] == 5, "'排序' 错误"c);
        assert(排序后数组[4] == 6, "'排序' 错误"c);
        assert(排序后数组[5] == 6, "'排序' 错误"c);
        assert(排序后数组[6] == 7, "'排序' 错误"c);
        dstring strffff = "asdfsdffdgodd"d;
        正则_!(dchar) re = ·regex(r"[a|o]"d, "g"c);
        strffff = re.p·替换全部(strffff, (匹配结果组_!(dstring) cap) {
            if (cap.hit() == "a"d) {
                return "A"d ~ cap.hit();
            }
            else {
                return "B"d ~ cap.hit();
            }
        });
        正则_!(dchar) re2 = ·regex(r"[A|B]"d, "g"c);
        strffff = re2.p·替换全部(strffff, "SS"d);
        assert(strffff == "SSasdfsdffdgSSodd"d, "'替换全部' 错误"c);
    }
    main();
}
